﻿using Microsoft.IdentityModel.Tokens;

namespace _4_NovaAdmin.Web.Core.Authorizations;

public class TokenHandler : ITokenHandler, IScopedDependency
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly JWTOptions _jwtSettings;
    private readonly JwtSecurityTokenHandler _tokenHandler = new();
    private readonly ILogger<TokenHandler> _logger;
    public TokenHandler(
        IHttpContextAccessor httpContextAccessor,
        IOptions<JWTOptions> jwtOptions,
        ILogger<TokenHandler> logger)
    {
        _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
        _jwtSettings = jwtOptions?.Value ?? throw new ArgumentNullException(nameof(jwtOptions));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <summary>
    /// 生成 JWT Token
    /// </summary>
    /// <param name="claims">用户声明信息</param>
    public string CreateToken(IEnumerable<Claim> claims)
    {
        // 1. 组装 Claims
        var jwtClaims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
        };
        jwtClaims.AddRange(claims);

        // 2. 创建密钥
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.IssuerSigningKey));
        var creds = new SigningCredentials(key, GetSecurityAlgorithm(_jwtSettings.Algorithm));

        // 3. 创建 Token 描述
        var token = new JwtSecurityToken(
            issuer: _jwtSettings.ValidIssuer,
            audience: _jwtSettings.ValidAudience,
            claims: jwtClaims,
            notBefore: DateTime.UtcNow,
            expires: DateTime.UtcNow.AddHours(_jwtSettings.ExpiresInHours),
            signingCredentials: creds
        );

        // 4. 生成 Token
        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    /// <summary>
    /// 从 Token 中解析用户信息
    /// </summary>
    public async Task<ClaimsPrincipal?> DecodeToken()
    {
        try
        {
            var token = GetCurrentToken();
            var tokenHandler = new JwtSecurityTokenHandler();
            var validationParameters = GetTokenValidationParameters();

            return tokenHandler.ValidateToken(token, validationParameters, out _);
        }
        catch
        {
            return null;
        }
    }

    /// <summary>  
    /// 从 Token 中解析用户信息  
    /// </summary>  
    /// <param name="key">要获取的 Claim 的键</param>  
    /// <returns>指定键对应的值，如果未找到则返回 null</returns>  
    public async Task<object> DecodeToken(string key)
    {
        try
        {
            var token = GetCurrentToken();
            var tokenHandler = new JwtSecurityTokenHandler();
            var validationParameters = GetTokenValidationParameters();

            var principal = tokenHandler.ValidateToken(token, validationParameters, out _);

            // 提取指定的值  
            var claim = principal.FindFirst(key);
            return claim?.Value;
        }
        catch
        {
            return null;
        }
    }

    /// <summary>
    /// 获取当前请求的 Token
    /// </summary>
    private string GetCurrentToken()
    {
        var header = _httpContextAccessor.HttpContext?.Request.Headers["Authorization"].FirstOrDefault();
        return header?.Replace("Bearer ", "");
    }

    /// <summary>
    /// 安全解析 Claim 值
    /// </summary>
    public async Task<T> GetClaimValueAsync<T>(string claimType)
    {
        var principal = await ValidateTokenAsync();
        var claim = principal.FindFirst(claimType) ?? throw new SecurityTokenException($"Claim {claimType} not found");

        try
        {
            return ConvertValue<T>(claim.Value);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Claim value conversion failed for {claimType}");
            throw new InvalidCastException($"Cannot convert '{claim.Value}' to {typeof(T).Name}", ex);
        }
    }

    private static T ConvertValue<T>(string value)
    {
        Type targetType = typeof(T);
        Type underlyingType = Nullable.GetUnderlyingType(targetType) ?? targetType;

        // 处理 Guid 类型（包括可空类型）
        if (underlyingType == typeof(Guid))
        {
            if (string.IsNullOrEmpty(value) && targetType == typeof(Guid?))
                return default!;

            return (T)(object)Guid.Parse(value);
        }

        // 处理枚举类型
        if (underlyingType.IsEnum)
        {
            return (T)Enum.Parse(underlyingType, value);
        }

        // 使用 TypeConverter 处理其他类型
        TypeConverter converter = TypeDescriptor.GetConverter(underlyingType);
        if (converter.CanConvertFrom(typeof(string)))
        {
            return (T)converter.ConvertFromString(value)!;
        }

        // 回退到 Convert.ChangeType
        return (T)Convert.ChangeType(value, underlyingType);
    }

    /// <summary>
    /// 验证 Token 有效性
    /// </summary>
    private async Task<ClaimsPrincipal> ValidateTokenAsync()
    {
        var token = GetCurrentToken();
        if (string.IsNullOrEmpty(token))
        {
            throw new SecurityTokenException("Authorization token is missing");
        }

        try
        {
            var validationParameters = GetTokenValidationParameters();
            return _tokenHandler.ValidateToken(token, validationParameters, out _);
        }
        catch (SecurityTokenException ex)
        {
            _logger.LogError(ex, "Token validation failed");
            throw;
        }
    }

    /// <summary>
    /// 获取 Token 验证参数（与鉴权配置一致）
    /// </summary>
    public TokenValidationParameters GetTokenValidationParameters()
    {
        return new TokenValidationParameters
        {
            ValidateIssuerSigningKey = _jwtSettings.ValidateIssuerSigningKey,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.IssuerSigningKey)),
            ValidateIssuer = _jwtSettings.ValidateIssuer,
            ValidIssuer = _jwtSettings.ValidIssuer,
            ValidateAudience = _jwtSettings.ValidateAudience,
            ValidAudience = _jwtSettings.ValidAudience,
            ValidateLifetime = _jwtSettings.ValidateLifetime,
            ClockSkew = TimeSpan.FromSeconds(_jwtSettings.ClockSkew),
            RequireExpirationTime = _jwtSettings.RequireExpirationTime
        };
    }

    private string GetSecurityAlgorithm(string algorithm)
    {
        return algorithm switch
        {
            "HS256" => SecurityAlgorithms.HmacSha256,
            "HS384" => SecurityAlgorithms.HmacSha384,
            "HS512" => SecurityAlgorithms.HmacSha512,
            _ => SecurityAlgorithms.HmacSha256
        };
    }

}
